﻿using UnityEngine;
using System;
using System.Threading;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.IO;
using System.Text;
using IMIForUnity;
using UnityEngine.UI;

namespace IMIForUnity
{
	public  class ImiBodyMeaMgr : MonoBehaviour,IImiManager
	{

		protected static ImiBodyMeaMgr instance;

		bool showGUI = true;
		private ImiManager imiManager;


		bool enableProcess = false;


		//for showing the result of bodymea.
		private byte[] usersDepthColors;
		public Texture2D customDepthTex;
		public bool ShowBodyMeaData = true;
		public RawImage depthView;

		public static ImiBodyMeaMgr Instance {
			get {
				if (instance == null) {
					instance = (ImiBodyMeaMgr)FindObjectOfType (typeof(ImiBodyMeaMgr));
					
					
					if (instance == null) {
						Debug.LogError ("An instance of " + typeof(ImiBodyMeaMgr) +
						" is needed in the scene, but there is none.");
					}
				}
				
				return instance;
			}
		}

		bool enableFrontTimer = false;
		bool isDuringFrontMeasure = false;
		bool isFrontMeaFinished = false;
		float FronttimeCount = 0f;
		float FronttimeSum = 8f;

		float FronttimeCountFixed = 0f;
		float FronttimeSumFixed = 11f;

		public float mRealTimeSinceStartup = 0f;

		bool enableSideTimer = false;
		bool isDuringSideMeasure = false;
		bool isSideMeaFinished = false;
		float sidetimeCount = 0f;
		float sidetimeSum = 8f;

		float sidetimeCountFixed = 0f;
		float sidetimeSumFixed = 11f;

		float sidetimeSmallCount0 = 0f;
		float sidetimeSmallSum = 0.7f;

		//Since 2017 03 21.
		/// <summary>
		/// We need a "PreSideStage" after triggering the BeginMeasureSide () but before Real side process stage.
		/// During the "PreSideStage" , we detect if the player is sidefacing the device.
		/// If he does, we trigger real side process.
		/// If not we give out the vector of side direction and tells the player to side face the dev. 
		/// Simultaneously, we trigger a timer. When this timer count is over, we trigger the Real Side process
		/// no matter the guy really face the dev or not.
		/// </summary>
		float preSideTimeSum=8f;
		float preSideTimeCount=0f;



		private BodyMeaMgrImp mBodyMeaMgrImp;

		public BodyMeaMgrImp MBodyMeaMgrImp {
			get {
				return mBodyMeaMgrImp;
			}
			set {
				mBodyMeaMgrImp = value;
			}
		}


		public bool isShowingDepthValueOfCertainpoint;
		public int detphValPixposX = 0;
		public int detphValPixposY = 0;
		public int depthVal = 0;


		enum BODYMEASTATE
		{
			IDLE0,
			FRONT,
			IDLE1,
			SIDE
		}

		BODYMEASTATE mbodymeaState = BODYMEASTATE.IDLE0;

		public GameObject SideDireObj;


		void Start ()
		{
			SideDireObj.SetActive(false);
			enableProcess = false;

			if (ShowBodyMeaData) {
				initCustomTexture ();
			}
			showGUI = true;
			if (instance == null) {
				instance = this;
			}


			imiManager = ImiManager.GetInstance ();
			imiManager.OnDeviceOpened += OnDeviceOpened;
			imiManager.OnDeviceClosed += OnDeviceClosed;
//			imiManager.OpenDevice ();



		
		}


		void initCustomTexture ()
		{
			customDepthTex = new Texture2D (640, 480, TextureFormat.RGBA32, false);
			usersDepthColors = new byte[640 * 480 * 4];
		}

		void OnDeviceOpened (object sende, EventArgs args)
		{
			//Do something when device is opened
			Debug.Log("-------------------Begin init resources for BodyMea");

			if (imiManager.IsDeviceOpened () && mBodyMeaMgrImp == null) 
			{
				mBodyMeaMgrImp = new BodyMeaMgrImp (ImiManagerImp.GetInstance ().playerInfos, usersDepthColors);
				ImiManager.GetInstance ().SetDepthDataProcessor (mBodyMeaMgrImp.BodyMea);
				mBodyMeaMgrImp.AllUsersDepthData = usersDepthColors;
				if (depthView != null) 
				{
					depthView.texture = customDepthTex;
				}
			}
			enableProcess = true;
		}

		void OnDeviceClosed (object sende, EventArgs args)
		{
			enableProcess = false;
		}



		public  void ClearPrevData ()
		{
			enableFrontTimer = false;
			enableSideTimer = false;
			isFrontMeaFinished = false;
			isSideMeaFinished = false;
			//mBodyMeaMgrImp.isDuringSideProcess = false;


			mBodyMeaMgrImp.ClearPrevData ();
//			waistSmthWideness.ClearAll();
//			heightSmoothTool.ClearAll();
			mbodymeaState = BODYMEASTATE.IDLE0;
		}


		//for measuring datas when user front facing the device
		public  void BeginMeasureFront ()
		{
			mBodyMeaMgrImp.enableFrontProcess = true;
			mBodyMeaMgrImp.enableSideProcess = false;
			enableFrontTimer = true;
			enableSideTimer = false;
			FronttimeCountFixed = 0f;

			isFrontMeaFinished = false;

			mbodymeaState = BODYMEASTATE.FRONT;
			//measure the front part of the body
		}


		//for measuring datas when user side facing the device
		public  void BeginMeasureSide ()
		{
			mBodyMeaMgrImp.enableFrontProcess = false;
			mBodyMeaMgrImp.enableSideProcess = true;
			sidetimeCountFixed = 0f;
			preSideTimeCount=0f;
			enableFrontTimer = false;
			enableSideTimer = true;
			isSideMeaFinished = false;			
			mbodymeaState = BODYMEASTATE.SIDE;
			mBodyMeaMgrImp.SetPlayerSideRecheckTimer (true);

			//Since 2017 03 17, we set the mBodyMeaMgrImp.enableMeaSide to false in the beginning of side measure.
			mBodyMeaMgrImp.enableMeaSide=false;

		}

		/// 0，代表未开始  1，全范围无人被detect到   2，有人被detect到， 但无人在指定位置（now 指定位置是中心线左右30 cm内）
		/// 3，指定位置有人，但没有侧面朝向设备， 4，测量中， 5， 测量成功
		/// ///   6, not yet designed, possible in future. In case the hands not put down and stick to both sides of body.
		public int  GetFrontState ()
		{
			if (isFrontMeaFinished) {
				if (!mBodyMeaMgrImp.isChestWidthNormal ()) {
					return 8;
				}
				if (!mBodyMeaMgrImp.isWaistWidthNormal ()) {
					return 9;
				}
				if (!mBodyMeaMgrImp.isHipsWidthNormal ()) {
					return 10;
				}

				if (
					!((mBodyMeaMgrImp.isHeightNormal ()) &&
					(mBodyMeaMgrImp.isShoulderWidthNormal ()) &&
					(mBodyMeaMgrImp.isArmlengthNormal ()) &&
					(mBodyMeaMgrImp.isLeglengthNormal ()))) {
					return 11;
				}

				return 5;
			}


			if (!enableFrontTimer) {
				return 0;
			}

			if (!mBodyMeaMgrImp.isPlayerDetected) {
				return 1;
			}
				
			if (!mBodyMeaMgrImp.isPlayerInsideArea) {
				return 2;
			}

			if (!mBodyMeaMgrImp.isPlayerFrontToDevice) {
				return 3;
			}		
			return 4;		
		}

		public int  GetSideState ()
		{
			if (isSideMeaFinished) {
				if (!mBodyMeaMgrImp.isChestThickNormal ()) {
					return 8;
				}
				if (!mBodyMeaMgrImp.isWaistThicknessNormal ()) {
					return 9;
				}
				if (!mBodyMeaMgrImp.isHipsThicknessNormal ()) {
					return 10;
				}


				return 5;
			}
			if (!enableSideTimer) {
				return 0;
			}
			
			if (!mBodyMeaMgrImp.isPlayerDetected) {
				return 1;
			}
			
			if (!mBodyMeaMgrImp.isPlayerInsideArea) {
				return 2;
			}
			
			if (!mBodyMeaMgrImp.isPlayerSideToDevice) {
				if (mBodyMeaMgrImp.isPlayerSideToDeviceALittleLeft) {
					return 6;
				}

				if (mBodyMeaMgrImp.isPlayerSideToDeviceALittleRight) {
					return 7;
				}

				return 3;
			}		

			return 4;

		}




		public float GetBodyHeight ()
		{
			return (mBodyMeaMgrImp.PlayerHeight) / 1000f;
		}

		public float GetWaistWidth ()
		{
			return mBodyMeaMgrImp.waistWideness;
		}

		public float GetWaistThickness ()
		{
			return mBodyMeaMgrImp.waistThickness;
		}

		public float GetChestWidth ()
		{
			return mBodyMeaMgrImp.chestWidth;
		}

		public float GetChestThickness ()
		{
			return mBodyMeaMgrImp.chestThickness;
		}

		public float GetHipsWidth ()
		{
			return mBodyMeaMgrImp.hipsWidth;
		}

		public float GetHipsThickness ()
		{
			return mBodyMeaMgrImp.hipsThickness;
		}


		public float GetHeadShoulderDeltaXZRatio ()
		{
			return mBodyMeaMgrImp.HeadShoulderDeltaXZRatio;
		}


		public  Vector3 SideVec()
		{
			if(mBodyMeaMgrImp!=null)
			{
				float m= mBodyMeaMgrImp.HeadShoulderDeltaXZRatio;
				Vector3 sideObjDire=new Vector3(1,m,0).normalized;
				return sideObjDire;
			}

			return new Vector3(1,0,0);
		}
		
		
		public float GetChestGirth ()
		{

			float g = mBodyMeaMgrImp.GetChestGirth ();
			if (g <= 0) {
				Debug.LogError ("GetChestGirth length not correct : " + g.ToString () + "   "
				);
			}



			return g;
		}

		
		public	float GetWaistGirth ()
		{

			float g = mBodyMeaMgrImp.GetWaistGirth ();

			if (g <= 0) {
				Debug.LogError ("GetWaistGirth length not correct : " + g.ToString () + "   "
				);
			}

			return g;
		}

		
		public	float GetHipsGirth ()
		{
			float g = mBodyMeaMgrImp.GetHipsGirth ();
			if (g <= 0) {
				Debug.LogError ("GetHipsGirth length not correct : " + g.ToString () + "   "
				);
			}

			return g;
		}


		public	float GetArmLength ()
		{
			return mBodyMeaMgrImp.ArmLength;

		}

		
		public	float GetLegLength ()
		{
			return (mBodyMeaMgrImp.LegLength) / 1000f;
		}

		
		public	float GetShoulderWidth ()
		{
			return mBodyMeaMgrImp.ShoulderWidth;

		}

		bool tmpDuringSide = false;
		public void Update ()
		{
			if (enableProcess) {
				mBodyMeaMgrImp.isShowMeaDataOnColorMap = ShowBodyMeaData;
				if (isShowingDepthValueOfCertainpoint) {
					depthVal = mBodyMeaMgrImp.GetDepthValOfCertainPixPos (detphValPixposX, detphValPixposY, isShowingDepthValueOfCertainpoint);
				}


				if (ShowBodyMeaData) {
					UpdateDepthTex ();
				}



				if ((Input.GetKey (KeyCode.LeftAlt)) || (Input.GetKey (KeyCode.RightAlt))) {
					if (Input.GetKeyDown (KeyCode.G)) {
						showGUI = !showGUI;
					}
				}
				if ((Input.GetKey (KeyCode.LeftAlt)) || (Input.GetKey (KeyCode.RightAlt))) {
					if (Input.GetKeyDown (KeyCode.D)) {
						mBodyMeaMgrImp.heightOffsetY += 10;
						Debug.LogWarning ("............................into A");
						PlayerPrefs.SetFloat ("heightOffsetY", mBodyMeaMgrImp.heightOffsetY);

					}
				}

				if ((Input.GetKey (KeyCode.LeftAlt)) || (Input.GetKey (KeyCode.RightAlt))) {
					if (Input.GetKeyDown (KeyCode.E)) {
						mBodyMeaMgrImp.heightOffsetY -= 10;
						PlayerPrefs.SetFloat ("heightOffsetY", mBodyMeaMgrImp.heightOffsetY);
					}
				}



				bool tmpDuringFront = false;
				if (enableFrontTimer) {
					if (FronttimeCount < FronttimeSum) {
						if (mBodyMeaMgrImp.isDuringFrontProcess) {
							FronttimeCount += Time.deltaTime;
							tmpDuringFront = true;
						}
					} else {
						ConcludeTimerFront ();
					}	


					if (FronttimeCountFixed < FronttimeSumFixed) {
						FronttimeCountFixed += Time.deltaTime;
					
					} else {
						ConcludeTimerFront ();
					}	





				}
				isDuringFrontMeasure = tmpDuringFront;
			


			//During side measurement.
				tmpDuringSide = false;
				if (enableSideTimer) {
					if(!mBodyMeaMgrImp.enableMeaSide)
					{
//						if(!SideDireObj.activeSelf)
//						{
//							SideDireObj.SetActive(true);
//						}
//						float m= mBodyMeaMgrImp.HeadShoulderDeltaXZRatio;
//						Vector3 sideObjDire=new Vector3(1,m,0);
//						SideDireObj.transform.up=sideObjDire.normalized;

						if(preSideTimeCount<preSideTimeSum)
						{
							preSideTimeCount+=Time.deltaTime;
						}
						else
						{
							preSideTimeCount=0f;
							mBodyMeaMgrImp.enableMeaSide=true;
						}

					}
					else
					{
						if(SideDireObj.activeSelf)
						{
							SideDireObj.SetActive(false);
						}
					   InRealSideProcess();
					}

				}
			
				isDuringSideMeasure = tmpDuringSide;

			}
		}


		void InRealSideProcess()
		{
			if (sidetimeCount < sidetimeSum) {
				
				//After 2017 03 17 , we annotate several following lines, for we need to test the side postrue directly.
				//						if (GetSideState () < 3) {
				//							mBodyMeaMgrImp.isDuringSideProcess = false;
				//						}
				
				
				//if (mBodyMeaMgrImp.isDuringSideProcess) {
				sidetimeCount += Time.deltaTime;
				tmpDuringSide = true;
				//}
				
				
			} else {
				ConcludeTimerSide ();
			}	
			
			
			if (sidetimeCountFixed < sidetimeSumFixed) {
				
				sidetimeCountFixed += Time.deltaTime;
				
			} else {
				ConcludeTimerSide ();
			}		

		}



		void ConcludeTimerFront ()
		{
			FronttimeCount = 0f;
			FronttimeCountFixed = 0f;
			enableFrontTimer = false;
			mBodyMeaMgrImp.enableFrontProcess = false;


			mBodyMeaMgrImp.ConcludeHeight ();
			mBodyMeaMgrImp.ConcludeArmLength ();
			mBodyMeaMgrImp.ConcludeLegLength ();
			mBodyMeaMgrImp.ConcludeShoulderWidth ();
			mBodyMeaMgrImp.ConcludeChestWidth ();
			mBodyMeaMgrImp.ConcludeWaistWidth ();
			mBodyMeaMgrImp.ConcludeHipsWidth ();
			isFrontMeaFinished = true;
			//frontMeaEnd (1);		
		}



		void  ConcludeTimerSide ()
		{
			sidetimeCount = 0f;
			sidetimeCountFixed = 0f;
			enableSideTimer = false;

			mBodyMeaMgrImp.enableSideProcess = false;
			mBodyMeaMgrImp.isDuringSideProcess = false;
		

			mBodyMeaMgrImp.ConcludeChestThick ();
			mBodyMeaMgrImp.ConcludeWaistThick ();
			mBodyMeaMgrImp.ConcludeHipsThick ();
			mBodyMeaMgrImp.SetPlayerSideRecheckTimer (false);
			isSideMeaFinished = true;
			//frontMeaEnd (1);		
		}



		public bool textureReady = false;

		private void UpdateDepthTex ()
		{
			if (textureReady) {
				customDepthTex.LoadRawTextureData (usersDepthColors);
				customDepthTex.Apply ();
				
				textureReady = false;
			}
		}





















		void OnGUI ()
		{

			if ((showGUI) && (mBodyMeaMgrImp != null)) {
				//		GUILayout.Label("origHeadDepth:   "+depthheadOrig.ToString());
				//		GUILayout.Label("HeadDepth1:   "+depthhead1.ToString());
				//		GUILayout.Label("HeadDepth2:   "+depthhead2.ToString());
				//		GUILayout.Label("HeadDepth3:   "+d epthhead3.ToString());
				//		GUILayout.Label("HeadDepth4:   "+depthhead4.ToString());
				//		GUILayout.Label("HeadDepth5:   "+depthhead5.ToString());

				//		GUILayout.Label("HeadOrigPixPos:   "+headOrigPixPos.ToString());
				//		GUILayout.Label("headTopPixPosY:   "+headTopPixPosY.ToString());
				//		GUILayout.Label("HeadOrigDepth:   "+headOrigDepth.ToString());
				GUI.skin.label.normal.textColor = Color.red;
				GUI.skin.label.fontSize = 20;

//			switch (mbodymeaState) {
//			case BODYMEASTATE.IDLE0:
//				GUILayout.Label ("IN Idle State 0");
//				break;
//			case BODYMEASTATE.FRONT:
//				GUILayout.Label ("During Measurement State");	
				//	GUILayout.Label ("Enable frontprocess :   " +  mBodyMeaMgrImp.enableFrontProcess.ToString ());
		

				GUILayout.Label ("Total players count in queue :   " +  mBodyMeaMgrImp.GetPlayerQueueCount());
				GUILayout.Label ("Depth Val of pix pos :   " + detphValPixposX.ToString () + "   " + detphValPixposY.ToString () + "   " + depthVal.ToString ());

				GUILayout.Label ("Player Height In Measure :   " + mBodyMeaMgrImp.playerHeight2.ToString ());
				GUILayout.Label ("Player Height Offset :   " + mBodyMeaMgrImp.heightOffsetY.ToString ());
				GUILayout.Label ("Front Timer :   " + FronttimeCount.ToString () + "   " +
				"Front Timer Fixed: " + FronttimeCountFixed.ToString ());
				GUILayout.Label ("PreSide Timer :   " + preSideTimeCount.ToString ()); 

				GUILayout.Label ("Side Timer :   " + sidetimeCount.ToString () + "   " +
				"Side Timer Fixed: " + sidetimeCountFixed.ToString ());

//
//			GUILayout.Label ("Player Chest Width :   " + GetChestWidth().ToString ());
//			GUILayout.Label ("Player chest Width in Mea :   " + mBodyMeaMgrImp.chestWidthtmp.ToString ());
//			GUILayout.Label ("Player Waist Width in Mea :   " + mBodyMeaMgrImp.tmpWid.ToString ());
//			GUILayout.Label ("Player Hips Width in Mea :   " + mBodyMeaMgrImp.hipsWidetmp.ToString ());
//GUILayout.Label ("Player Shoulder Width in Mea :   " + mBodyMeaMgrImp.shoulderWTmp.ToString ());
			
//				GUILayout.Label ("Player Hips Width :   " + GetHipsWidth().ToString ());
//				break;
//
//			case BODYMEASTATE.SIDE:
//				GUILayout.Label ("During Measurement Side State");		
//				GUILayout.Label ("Player waist thickness :   " + GetWaistThickness().ToString ());
//				GUILayout.Label ("Player chest thickness :   " + GetChestThickness().ToString ());
//				GUILayout.Label ("Player hips thickness :   " + GetHipsThickness().ToString ());
//				break;
//
//			case BODYMEASTATE.IDLE1:
//				GUILayout.Label ("Player Height :   " + GetBodyHeight().ToString ());	
//				break;
//			default:
//				break;
//			}
			
				//GUILayout.Label ("Press Ctrl A and Ctrl B to increase or decrease Height offset");
			
				GUILayout.Label ("Player Center pixel :   " + mBodyMeaMgrImp.DoesCenterPixleLineContainsPlayerPixel (false).ToString ());


				GUILayout.Label ("Player head shoulder Delta XZ :   " + GetHeadShoulderDeltaXZRatio ().ToString ());
				GUILayout.Label ("chest Width :   " + GetChestWidth ().ToString ());
				GUILayout.Label ("chest Thickness :   " + GetChestThickness ().ToString ());

				GUILayout.Label ("Waist Width :   " + GetWaistWidth ().ToString ());
				GUILayout.Label ("Waist Thickness :   " + GetWaistThickness ().ToString ());
				GUILayout.Label ("Hips Width :   " + GetHipsWidth ().ToString ());
				GUILayout.Label ("Hips Thickness :   " + GetHipsThickness ().ToString ());



				GUILayout.Label ("Front State :   " + GetFrontState ().ToString ());
				GUILayout.Label ("Side State :   " + GetSideState ().ToString ());

				if ((GetFrontState () == 5) && (GetSideState () == 5)) {
					GUILayout.Label ("Player Height:   " + GetBodyHeight ().ToString ());
					GUILayout.Label ("Player chestgirth:   " + GetChestGirth ().ToString ());
					GUILayout.Label ("Player waistgirth:   " + GetWaistGirth ().ToString ());
					GUILayout.Label ("Player hipsgirth:   " + GetHipsGirth ().ToString ());
					GUILayout.Label ("Player armlength:   " + GetArmLength ().ToString ());
					GUILayout.Label ("Player shoulderwidth:   " + GetShoulderWidth ().ToString ());
					GUILayout.Label ("Player leglength:   " + GetLegLength ().ToString ());
				}
			
			}
		}

	}
}
